from fastapi import FastAPI, Depends, HTTPException, status, Request
from fastapi.staticfiles import StaticFiles
from fastapi.templating import Jinja2Templates
from fastapi.responses import HTMLResponse
from fastapi.security import OAuth2PasswordBearer, OAuth2PasswordRequestForm
import logging

# 配置日志
logging.basicConfig(level=logging.INFO)
logger = logging.getLogger(__name__)
from sqlalchemy import create_engine, Column, Integer, String, Boolean
from sqlalchemy.ext.declarative import declarative_base
from sqlalchemy.orm import sessionmaker, Session
from passlib.context import CryptContext
from datetime import datetime, timedelta
import jwt
from jwt import PyJWTError
import os
from pydantic import BaseModel
from typing import Optional, List

# 创建FastAPI应用
app = FastAPI(title="用户管理系统API", description="管理员可以管理普通用户，普通用户可以修改自己的信息")

# 挂载静态文件目录
app.mount("/static", StaticFiles(directory="static"), name="static")

# 配置模板目录
templates = Jinja2Templates(directory="templates")

# 配置数据库连接
SQLALCHEMY_DATABASE_URL = "sqlite:///" + os.path.join(os.path.dirname(os.path.abspath(__file__)), "data", "users.db")
engine = create_engine(SQLALCHEMY_DATABASE_URL, connect_args={"check_same_thread": False})
SessionLocal = sessionmaker(autocommit=False, autoflush=False, bind=engine)
Base = declarative_base()

# 密码哈希配置
pwd_context = CryptContext(schemes=["bcrypt"], deprecated="auto")

# OAuth2配置
SECRET_KEY = "your-secret-key"  # 实际生产环境应使用环境变量设置
ALGORITHM = "HS256"
ACCESS_TOKEN_EXPIRE_MINUTES = 30

# 用户模型
class UserDB(Base):
    __tablename__ = "user"

    id = Column(Integer, primary_key=True, index=True)
    username = Column(String(80), unique=True, index=True, nullable=False)
    password = Column(String(120), nullable=False)
    is_admin = Column(Boolean, default=False, nullable=False)
    full_name = Column(String(100), nullable=True)
    gender = Column(String(10), nullable=True)
    nickname = Column(String(80), nullable=True)
    email = Column(String(120), nullable=True)
    phone = Column(String(20), nullable=True)

# Pydantic模型 - 用于请求和响应
class UserBase(BaseModel):
    full_name: Optional[str] = None
    gender: Optional[str] = None
    nickname: Optional[str] = None
    email: Optional[str] = None
    phone: Optional[str] = None

class UserCreate(UserBase):
    username: str
    password: str

class UserUpdate(UserBase):
    password: Optional[str] = None
    is_admin: Optional[bool] = None

class User(UserBase):
    id: int
    username: str
    is_admin: bool

    class Config:
        orm_mode = True

class Token(BaseModel):
    access_token: str
    token_type: str

class TokenData(BaseModel):
    username: Optional[str] = None

# 数据库依赖
def get_db():
    db = SessionLocal()
    try:
        yield db
    finally:
        db.close()

# OAuth2依赖
oauth2_scheme = OAuth2PasswordBearer(tokenUrl="token")

# 密码工具函数
def verify_password(plain_password, hashed_password):
    # 确保密码不超过bcrypt的72字节限制
    plain_password = plain_password[:72]
    return pwd_context.verify(plain_password, hashed_password)

def get_password_hash(password):
    # 确保密码不超过bcrypt的72字节限制
    password = password[:72]
    return pwd_context.hash(password)

# 用户工具函数
def get_user(db, username: str):
    return db.query(UserDB).filter(UserDB.username == username).first()

def authenticate_user(db, username: str, password: str):
    try:
        user = get_user(db, username)
        if not user:
            return False
        
        # 首先尝试直接比较明文密码（适用于测试用户或未哈希的密码）
        if user.password == password:
            # 注意：在生产环境中应该考虑将明文密码转换为哈希密码
            return user
        
        # 尝试使用密码哈希验证
        try:
            if verify_password(password, user.password):
                return user
        except Exception as e:
            print(f"密码哈希验证出错: {e}")
            # 如果哈希验证失败，返回False
            return False
        
        return False
    except Exception as e:
        print(f"用户认证过程中出错: {e}")
        return False

def create_access_token(data: dict, expires_delta: Optional[timedelta] = None):
    to_encode = data.copy()
    if expires_delta:
        expire = datetime.utcnow() + expires_delta
    else:
        expire = datetime.utcnow() + timedelta(minutes=15)
    to_encode.update({"exp": expire})
    encoded_jwt = jwt.encode(to_encode, SECRET_KEY, algorithm=ALGORITHM)
    return encoded_jwt

async def get_current_user(token: str = Depends(oauth2_scheme), db: Session = Depends(get_db)):
    credentials_exception = HTTPException(
        status_code=status.HTTP_401_UNAUTHORIZED,
        detail="Could not validate credentials",
        headers={"WWW-Authenticate": "Bearer"},
    )
    try:
        payload = jwt.decode(token, SECRET_KEY, algorithms=[ALGORITHM])
        username: str = payload.get("sub")
        if username is None:
            raise credentials_exception
        token_data = TokenData(username=username)
    except PyJWTError:
        raise credentials_exception
    user = get_user(db, username=token_data.username)
    if user is None:
        raise credentials_exception
    return user

async def get_current_active_user(current_user: UserDB = Depends(get_current_user)):
    return current_user

async def get_current_admin_user(current_user: UserDB = Depends(get_current_active_user)):
    if not current_user.is_admin:
        raise HTTPException(
            status_code=status.HTTP_403_FORBIDDEN,
            detail="需要管理员权限"
        )
    return current_user

# 初始化数据库
def init_db():
    Base.metadata.create_all(bind=engine)
    db = SessionLocal()
    # 确保admin用户是管理员
    try:
        admin_user = db.query(UserDB).filter(UserDB.username == "admin").first()
        if admin_user and not admin_user.is_admin:
            admin_user.is_admin = True
            db.commit()
    except Exception as e:
        print(f"初始化数据库时出错: {e}")
        # 忽略错误，继续启动服务
    finally:
        db.close()

# API路由
@app.on_event("startup")
def on_startup():
    init_db()

# 提供登录页面
@app.get("/login", response_class=HTMLResponse)
def login_page(request: Request):
    return templates.TemplateResponse("login.html", {"request": request})

# 提供欢迎页面
@app.get("/welcome", response_class=HTMLResponse)
def welcome_page(request: Request):
    return templates.TemplateResponse("welcome.html", {"request": request})

# 默认路由重定向到登录页
@app.get("/", response_class=HTMLResponse)
def root(request: Request):
    return templates.TemplateResponse("login.html", {"request": request})

@app.post("/token", response_model=Token)
def login_for_access_token(form_data: OAuth2PasswordRequestForm = Depends(), db: Session = Depends(get_db)):
    logger.info(f"Login attempt for user: {form_data.username}")
    
    user = authenticate_user(db, form_data.username, form_data.password)
    if not user:
        logger.warning(f"Login failed for user: {form_data.username} - Incorrect username or password")
        raise HTTPException(
            status_code=status.HTTP_401_UNAUTHORIZED,
            detail="Incorrect username or password",
            headers={"WWW-Authenticate": "Bearer"},
        )
    
    logger.info(f"Login successful for user: {form_data.username}")
    access_token_expires = timedelta(minutes=ACCESS_TOKEN_EXPIRE_MINUTES)
    access_token = create_access_token(
        data={"sub": user.username}
    )
    return {"access_token": access_token, "token_type": "bearer"}

@app.get("/users/me", response_model=User)
def read_users_me(current_user: UserDB = Depends(get_current_active_user)):
    return current_user

@app.put("/users/me", response_model=User)
def update_users_me(user_update: UserUpdate, current_user: UserDB = Depends(get_current_active_user), db: Session = Depends(get_db)):
    # 更新用户信息
    if user_update.full_name is not None:
        current_user.full_name = user_update.full_name
    if user_update.gender is not None:
        current_user.gender = user_update.gender
    if user_update.nickname is not None:
        current_user.nickname = user_update.nickname
    if user_update.email is not None:
        current_user.email = user_update.email
    if user_update.phone is not None:
        current_user.phone = user_update.phone
    if user_update.password is not None:
        current_user.password = get_password_hash(user_update.password)
    
    db.commit()
    db.refresh(current_user)
    return current_user

@app.get("/users", response_model=List[User])
def read_users(skip: int = 0, limit: int = 100, current_user: UserDB = Depends(get_current_admin_user), db: Session = Depends(get_db)):
    users = db.query(UserDB).offset(skip).limit(limit).all()
    return users

@app.get("/users/{user_id}", response_model=User)
def read_user(user_id: int, current_user: UserDB = Depends(get_current_admin_user), db: Session = Depends(get_db)):
    db_user = db.query(UserDB).filter(UserDB.id == user_id).first()
    if db_user is None:
        raise HTTPException(status_code=404, detail="用户不存在")
    return db_user

@app.post("/users", response_model=User)
def create_user(user: UserCreate, current_user: UserDB = Depends(get_current_admin_user), db: Session = Depends(get_db)):
    db_user = get_user(db, username=user.username)
    if db_user:
        raise HTTPException(status_code=400, detail="用户名已存在")
    hashed_password = get_password_hash(user.password)
    db_user = UserDB(
        username=user.username,
        password=hashed_password,
        full_name=user.full_name,
        gender=user.gender,
        nickname=user.nickname,
        email=user.email,
        phone=user.phone
    )
    db.add(db_user)
    db.commit()
    db.refresh(db_user)
    return db_user

@app.put("/users/{user_id}", response_model=User)
def update_user(user_id: int, user: UserUpdate, current_user: UserDB = Depends(get_current_admin_user), db: Session = Depends(get_db)):
    db_user = db.query(UserDB).filter(UserDB.id == user_id).first()
    if db_user is None:
        raise HTTPException(status_code=404, detail="用户不存在")
    
    # 不允许修改管理员用户的管理员权限
    if db_user.is_admin and user.is_admin is False:
        raise HTTPException(status_code=403, detail="不能修改管理员用户的权限")
    
    # 更新用户信息
    if user.full_name is not None:
        db_user.full_name = user.full_name
    if user.gender is not None:
        db_user.gender = user.gender
    if user.nickname is not None:
        db_user.nickname = user.nickname
    if user.email is not None:
        db_user.email = user.email
    if user.phone is not None:
        db_user.phone = user.phone
    if user.password is not None:
        db_user.password = get_password_hash(user.password)
    # 只有管理员可以修改其他用户的管理员权限
    if user.is_admin is not None and current_user.is_admin:
        db_user.is_admin = user.is_admin
    
    db.commit()
    db.refresh(db_user)
    return db_user

@app.delete("/users/{user_id}", response_model=User)
def delete_user(user_id: int, current_user: UserDB = Depends(get_current_admin_user), db: Session = Depends(get_db)):
    db_user = db.query(UserDB).filter(UserDB.id == user_id).first()
    if db_user is None:
        raise HTTPException(status_code=404, detail="用户不存在")
    
    # 不允许删除管理员用户
    if db_user.is_admin:
        raise HTTPException(status_code=403, detail="不能删除管理员用户")
    
    db.delete(db_user)
    db.commit()
    return db_user